[TDR Generic表][Go SDK]查询单条数据

1. 接口说明

从数据库表中查询一条记录,记录不存在会报错

2. 版本要求

无特殊要求,所有版本都提供了该接口。

3. 准备工作

参见准备工作文档,完成使用该接口前的准备工作,并创建如下TDR Generic表。service_info表service_info.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<metalib name="service_info" tagsetversion="1" version="1">
    <struct name="service_info" version="1" primarykey="gameid,envdata,name,expansion" splittablekey="gameid" >
        <entry name="gameid"               type="string"     size="128" desc="gameapp id"/>
        <entry name="envdata"             type="string"     size="1024" desc="env环境信息" />
        <entry name="name"                 type="string"     size="1024" desc="名字" />
        <entry name="expansion"          type="string"     size="1024" desc="扩展字段" />
        <entry name="filterdata"        type="string"     size="1024" desc="过滤标签" />
        <entry name="updatetime"        type="uint64"     desc="最近更新时间,单位ms" />
        <entry name="inst_max_num"      type="uint64"     desc="最大实例个数" />
        <entry name="inst_min_num"      type="uint64"     desc="最小实例个数" />
        <entry name="routeinfo_len"     type="uint"   defaultvalue="0" desc="路由规则信息长度" />
        <entry name="routeinfo"         type="char"   count="1024" refer="routeinfo_len" desc="路由规则信息" />
        <index name="index_gameid_envdata_name" column="gameid,envdata,name" />
        <index name="index_gameid_envdata" column="gameid,envdata" />
        <index name="index_gameid_name" column="gameid,name" />
        <index name="index_gameid" column="gameid" />
    </struct>
</metalib>

准备工作完成后,将会获得以下信息,这些信息在使用SDK时会被用到:

  1. 目录服务器地址列表
  2. 业务ID
  3. 业务访问密码
  4. 游戏区ID
  5. 数据表名

4. 示例代码

示例代码的基本执行过程:

  1. 定义表配置参数
  2. 设置日志配置;
  3. 创建客户端;
  4. 发送请求并处理响应;
  5. 销毁客户端。

客户端初始化示例:

package main

import (
    "fmt"
    "git.woa.com/gcloud_storage_group/tcaplus-go-api"
)

// 定义表配置参数
const (
  AppId                = uint64(2)
  ZoneId               = uint32(3)
  DirUrl               = "tcp://x.x.x.x:xxxx"
  Signature            = "xxxxxxxxxxxxx"
  TableName            = "service_info"
)

var client *tcaplus.Client

func main() {
  // 创建客户端
  client = tcaplus.NewClient()
  // 设置日志配置,logconf.xml文件设置了日志级别
  if err := client.SetLogCfg("./logconf.xml"); err != nil {
      fmt.Println(err.Error())
      return
  }

  // 连接TcaplusDB后端
  err := client.Dial(AppId, []uint32{ZoneId}, DirUrl, Signature, 60)
  if err != nil {
      fmt.Printf("init failed %v\n", err.Error())
      return
  }
  fmt.Printf("Dial finish\n")
  getExample()

  // 程序退出时调用Close销毁客户端
  client.Close()
}

4.1 同步调用示例(推荐)

同步调用编码最简单,通过多协程并发 示例目录

package main

import (
  "fmt"
  "git.woa.com/gcloud_storage_group/tcaplus-go-api/example/TDR/async/service_info"
)

func getExample() {
    //申请tdr结构体并赋值Key,最好调用tdr pkg的NewXXX函数,会将成员初始化为tdr定义的tdr默认值,
    // 不要自己new,自己new,某些结构体未初始化,存在panic的风险
    data := service_info.NewService_Info()
    data.Gameid = "dev"
    data.Envdata = "oa"
    data.Name = "com"
    // 同步发送请求并接收响应,响应消息会覆盖到data中
    err := client.DoGet(TableName, data, nil)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    fmt.Println(data)
}

4.2 同步调用示例2(推荐)

该示例同步调用方式比较类似C++接口的调用方式,需要创建请求和解析响应示例目录

package main

import (
  "fmt"
  "git.woa.com/gcloud_storage_group/tcaplus-go-api/example/TDR/async/service_info"
  "git.woa.com/gcloud_storage_group/tcaplus-go-api/protocol/cmd"
  "git.woa.com/gcloud_storage_group/tcaplus-go-api/terror"
  "time"
)

func getExample() {
  // 创建请求
  req, err := client.NewRequest(ZoneId, TableName, cmd.TcaplusApiGetReq)
  if err != nil {
    fmt.Printf("getExample NewRequest TcaplusApiGetReq failed %v\n", err.Error())
    return
  }

  //为request添加一条记录,(index只有在list表中支持,generic不校验)
  rec, err := req.AddRecord(0)
  if err != nil {
    fmt.Printf("getExample AddRecord failed %v\n", err.Error())
    return
  }

  //申请tdr结构体并赋值Key,最好调用tdr pkg的NewXXX函数,会将成员初始化为tdr定义的tdr默认值,
  // 不要自己new,自己new,某些结构体未初始化,存在panic的风险
  data := service_info.NewService_Info()
  data.Gameid = "dev"
  data.Envdata = "oa"
  data.Name = "com"
  //将tdr的数据设置到请求的记录中
  if err := rec.SetData(data); err != nil {
    fmt.Printf("SetData failed %v\n", err.Error())
    return
  }

  // 使用客户端同步发送请求并接收响应
  if resp, err := client.Do(req, time.Duration(2*time.Second)); err != nil {
    fmt.Printf("recv err %s\n", err.Error())
    return
  } else {
    // 获取响应消息的错误码
    tcapluserr := resp.GetResult()
    if tcapluserr != 0 {
      fmt.Printf("response ret errCode: %d, errMsg: %s", tcapluserr, terror.GetErrMsg(tcapluserr))
      return
    }

    // 从响应消息中提取记录
    fmt.Printf("getExample response success record count %d\n", resp.GetRecordCount())
    for i := 0; i < resp.GetRecordCount(); i++ {
      record, err := resp.FetchRecord()
      if err != nil {
        fmt.Printf("FetchRecord failed %s\n", err.Error())
        return
      }
      //通过GetData获取记录
      data := service_info.NewService_Info()
      if err := record.GetData(data); err != nil {
        fmt.Printf("record.GetData failed %s\n", err.Error())
        return
      }
      fmt.Printf("getExample response record data %+v, route: %s\n",
        data, string(data.Routeinfo[0:data.Routeinfo_Len]))
    }
  }
}

4.3 异步调用示例

异步发送可以使用较少的协程实现较大的并发,编码相对复杂,示例目录

package main

import (
  "fmt"
  "git.woa.com/gcloud_storage_group/tcaplus-go-api/example/TDR/async/service_info"
  "git.woa.com/gcloud_storage_group/tcaplus-go-api/logger"
  "git.woa.com/gcloud_storage_group/tcaplus-go-api/protocol/cmd"
  "git.woa.com/gcloud_storage_group/tcaplus-go-api/terror"
  "strconv"
  "time"
)

func getExample() {
  wg := sync.WaitGroup{}
  wg.Add(1)
  // 在另一协程处理响应消息
  go func() {
    defer wg.Done()
    for {
      // resp err 均为 nil 说明响应池中没有任何响应
      resp, err := client.RecvResponse()
      if err != nil {
        logger.ERR("RecvResponse error:%s", err)
        continue
      } else if resp == nil {
        time.Sleep(time.Microsecond * 5)
        continue
      }

      //带回请求的异步ID
      fmt.Printf("resp success, AsyncId:%d\n", resp.GetAsyncId())
      tcapluserr := resp.GetResult()
      if tcapluserr != 0 {
        fmt.Printf("response ret %s\n",
          "errCode: "+strconv.Itoa(tcapluserr)+", errMsg: "+terror.ErrorCodes[tcapluserr])
        return
      }
      //response中带有获取的记录
      fmt.Printf("response success record count %d\n", resp.GetRecordCount())
      for i := 0; i < resp.GetRecordCount(); i++ {
        record, err := resp.FetchRecord()
        if err != nil {
          fmt.Printf("FetchRecord failed %s\n", err.Error())
          return
        }
        //通过GetData获取记录
        data := service_info.NewService_Info()
        if err := record.GetData(data); err != nil {
          fmt.Printf("record.GetData failed %s\n", err.Error())
          return
        }
        fmt.Printf("response record data %+v, route: %s\n",
          data, string(data.Routeinfo[0:data.Routeinfo_Len]))
      }
      return
    }
  }()

  //创建Get请求
  req, err := client.NewRequest(ZoneId, TableName, cmd.TcaplusApiGetReq)
  if err != nil {
    fmt.Printf("NewRequest TcaplusApiGetReq failed %v\n", err.Error())
    return
  }

  //设置异步请求ID,异步请求通过ID让响应和请求对应起来
  req.SetAsyncId(667)
  //为request添加一条记录,(index只有在list表中支持,generic不校验)
  rec, err := req.AddRecord(0)
  if err != nil {
    fmt.Printf("AddRecord failed %v\n", err.Error())
    return
  }

  //申请tdr结构体并赋值Key,最好调用tdr pkg的NewXXX函数,会将成员初始化为tdr定义的tdr默认值,
  // 不要自己new,自己new,某些结构体未初始化,存在panic的风险
  data := service_info.NewService_Info()
  data.Gameid = "dev"
  data.Envdata = "oa"
  data.Name = "com"
  //将tdr的数据设置到请求的记录中
  if err := rec.SetData(data); err != nil {
    fmt.Printf("SetData failed %v\n", err.Error())
    return
  }

  // 异步发送请求
  if err := client.SendRequest(req); err != nil {
    fmt.Printf("SendRequest failed %v\n", err.Error())
    return
  }
  wg.Wait()
}

5. 请求对象(request)中的方法说明

注:此处未列出请求对象的其它方法,即表示该方法在查询数据的场景不需要使用,误用可能会导致报错

/**
  @brief  向请求中添加一条记录。
  @param [IN] index         用于List操作,通常>=0,表示该Record在所属List中的Index;
                            对于Generic操作,index无意义,设0即可
  @retval record.Record     返回记录指针
  @retval error               错误码
**/
AddRecord(index int32) (*record.Record, error)

/**
@brief  设置请求的异步事务ID,api会将其值不变地通过对应的响应消息带回来
@param  [IN] asyncId  请求对应的异步事务ID
**/
SetAsyncId(id uint64)

/**
@brief  设置请求的通用标志位,可以通过"按位或"操作同时设定多个值
@param  [IN]  flag. 请求标志位的值
@retval 0     设置成功
@retval <0    失败,返回对应的错误码。通常因为未初始化。
@note   有效的标志位包括:
*  TCAPLUS_FLAG_FETCH_ONLY_IF_MODIFIED:
*       "数据变更才取回"标志位。在发起读操作之前,用户代码通过 TcaplusServiceRecord::SetVersion()
*       带上本地缓存数据的版本号,并将此标志置位,那么存储端检测到当前数据与API本地缓存的数据版本
*       一致时,表明该记录未发生过修改,API缓存的数据是最新的,因此在响应中将不会携带实际的数据,
*       只是返回 TcapErrCode::COMMON_INFO_DATA_NOT_MODIFIED 的错误码
*
*       在请求中设置了此标志位之后,收到响应后应首先通过 TcaplusServiceResponse::GetFlags() 来获知
*       发送请求时是否设置了TCAPLUS_FLAG_FETCH_ONLY_IF_MODIFIED标志.
*
*       只有如下请求支持设置此标志:
*           TCAPLUS_API_GET_REQ,
*           TCAPLUS_API_LIST_GET_REQ,
*           TCAPLUS_API_LIST_GETALL_REQ
*
*  TCAPLUS_FLAG_FETCH_ONLY_IF_EXPIRED:
*       "数据过期才取回"标志位。在发起读操作之前,用户代码通过 SetExpireTime() 设定数据过期时间,
*       并将此标志置位,那么存储端若检测到记录在指定时间内发生过更新,则将数据返回,
*       否则不返回实际数据,只是返回 TcapErrCode::COMMON_INFO_DATA_NOT_MODIFIED 的错误码。
*
*       在请求中设置了此标志位之后,收到响应后应首先通过 TcaplusServiceResponse::GetFlags() 来获知
*       发送请求时是否设置了 TCAPLUS_FLAG_FETCH_ONLY_IF_EXPIRED 标志.
*
*       只有如下请求支持设置此标志:
*           TCAPLUS_API_BATCH_GET_REQ
*
*  TCAPLUS_FLAG_ONLY_READ_FROM_SLAVE
*       设置此标志后,读请求将会直接发送给Tcapsvr Slave 节点。
*       Tcapsvr Slave 通常比较空闲,设置此标志有助于充分利用Tcapsvr Slave 资源。
*
*       适用场景:
*                              对于数据实时性要求不高的读请求,
*                              包括generic表和list表的所有读请求以及batchget,遍历请求
*
*  TCAPLUS_FLAG_LIST_RESERVE_INDEX_HAVING_NO_ELEMENTS
*       设置此标志后,List表删除最后一个元素时需要保留index和version。
*       ListDelete ListDeleteBatch ListDeleteAll操作在删除list表最后一个元素时,
*          设置此标志在写入新的List记录时,版本号依次增长,不会被重置为1。
*
*       适用场景:
*                              业务需要确定某个表在删除最后一个元素时是否需要保留index和version
*                              主要涉及List表的使用体验
*
*/
SetFlags(flag int32) int

/**
  @brief  清理请求的通用标志位,可以通过"按位或"操作同时设定多个值
  @param  [IN]  flag. 请求标志位的值
  @retval 0     设置成功
  @retval <0    失败,返回对应的错误码。通常因为未初始化。
  @note   有效的标志位列表及详细解释请参考 SetFlags()
*/
ClearFlags(flag int32) int

/**
  @brief   获取请求的通用标志位
  @return  返回请求的通用标志位
  @note   有效的标志位列表及详细解释请参考 SetFlags()
*/
GetFlags() int32

/**
  @brief 设置需要查询或更新的Value字段名称列表,即部分Value字段查询和更新,可用于get、replace、update操作。
  @param [IN] valueNameList   需要查询或更新的字段名称列表
  @retval error                  错误码
  @note  在使用该函数设置字段名时,字段名只能包含value字段名,不能包含key字段名;对于数组类型的字段,
              refer字段和数组字段要同时设置或者同时不设置,否则容易数据错乱
**/
SetFieldNames(valueNameList []string) error

/**
@brief 设置用户缓存,响应消息将携带返回
@param [IN] userBuffer   用户缓存
@retval error               错误码
**/
SetUserBuff(userBuffer []byte) error

6. 响应对象(response)中的方法说明

注:此处未列出的响应对象的其它方法,即表示该方法在查询数据的场景不需要使用,误用可能会导致报错

/*
    @brief  获取响应结果
    @retval int tcaplus api自定义错误码。 0,表示请求成功;非0,有错误码,可从terror.GetErrMsg(int)得到错误消息
*/
GetResult() int

/*
    @brief  获取响应表名
    @retval string 响应消息对应的表名称
*/
GetTableName() string

/*
    @brief  获取响应appId
    @retval uint64 响应消息对应的appId
*/
GetAppId() uint64

/*
    @brief  获取响应zoneId
    @retval uint32 响应消息对应的zoneId
*/
GetZoneId() uint32

/*
    @brief  获取响应命令
    @retval int 响应消息命令字,cmd包中的响应命令字
*/
GetCmd() int

/*
    @brief  获取响应异步id,和请求对应
    @retval uint64 响应消息对应的异步id和请求对应
*/
GetAsyncId() uint64

/*
    @brief  获取本响应中结果记录条数
    @retval int 响应中结果记录条数
*/
GetRecordCount() int

/*
    @brief  从结果中获取一条记录
    @retval *record.Record 记录指针
    @retval error 错误码
*/
FetchRecord() (*record.Record, error)

/*
    @brief  获取响应消息中的用户缓存信息
    @retval []byte 用户缓存二进制,和请求消息中的buffer内容一致
*/
GetUserBuffer() []byte

7. 记录对象(record)中的方法说明

注:此处未列出的响应对象的其它方法,即表示该方法在查询数据的场景不需要使用,误用可能会导致报错

  • record的通用方法:
/**
    @brief  获取记录版本号
    @retval 记录版本号
    @note 对于Generic操作表示获取Record的版本;对于List操作表示获取Record所在List的版本。
**/
func (r *Record) GetVersion() int32
  • record可以基于TDR结构设置和提取记录
 /**
@brief  基于TDR描述设置record数据
@param [IN] data  基于TDR描述record接口数据,tdr的xml通过工具生成的go结构体,包含的TdrTableSt接口的一系列方法
@retval error     错误码
*/
func (r *Record) SetData(data TdrTableSt) error

/**
    @brief  基于TDR描述读取record数据
    @param [IN] data   基于TDR描述record接口数据,tdr的xml通过工具生成的go结构体,包含的TdrTableSt接口的一系列方法
    @retval error      错误码
**/
func (r *Record) GetData(data TdrTableSt) error
  • record同时支持通过SetKey SetValue接口设置记录中的字段,通过GetKey GetValue接口提取记录中的字段,但是SetKey、SetValue、GetKey、GetValue同SetData、GetData接口不可混用
    1. SetKey SetValue接口设置的数据,只能通过GetKey,GetValue接口读取;SetKey SetValue在使用batch命令时需要注意,record设置完成后调用Pack()把记录打包;SetData是一次性的,函数调用会自动打包,而SetKey SetValue不确定用户什么时候设置完最后一个字段,所以需要用户设置完kv后调用Pack()把记录打包;
    2. SetData接口设置的数据,只能通过GetData读取
/**
  @brief  key字段内容设置
  @param  [in] name   字段名称,最大长度32
  @param  [in] data   字段内容
  @retval error       错误码
*/
func (r *Record) SetKeyInt8(name string, data int8) error
func (r *Record) SetKeyInt16(name string, data int16) error
func (r *Record) SetKeyInt32(name string, data int32) error
func (r *Record) SetKeyInt64(name string, data int64) error
func (r *Record) SetKeyFloat32(name string, data float32) error
func (r *Record) SetKeyFloat64(name string, data float64) error
func (r *Record) SetKeyStr(name string, data string) error
func (r *Record) SetKeyBlob(name string, data []byte) error

/**
  @brief  value字段内容设置
  @param  [in] name   字段名称,最大长度32
  @param  [in] data   字段内容
  @retval error       错误码
*/
func (r *Record) SetValueInt8(name string, data int8) error
func (r *Record) SetValueInt16(name string, data int16) error
func (r *Record) SetValueInt32(name string, data int32) error
func (r *Record) SetValueInt64(name string, data int64) error
func (r *Record) SetValueFloat32(name string, data float32) error
func (r *Record) SetValueFloat64(name string, data float64) error
func (r *Record) SetValueStr(name string, data string) error
func (r *Record) SetValueBlob(name string, data []byte) error

/**
  @brief  key字段内容获取
  @param  [in] name   字段名称,最大长度32
  @retval data        字段内容
  @retval error       错误码
*/
func (r *Record) GetKeyInt8(name string) (int8, error)
func (r *Record) GetKeyInt16(name string) (int16, error)
func (r *Record) GetKeyInt32(name string) (int32, error)
func (r *Record) GetKeyInt64(name string) (int64, error)
func (r *Record) GetKeyFloat32(name string) (float32, error)
func (r *Record) GetKeyFloat64(name string) (float64, error)
func (r *Record) GetKeyStr(name string) (string, error)
func (r *Record) GetKeyBlob(name string) ([]byte, error)

/**
  @brief  value字段内容获取
  @param  [in] name   字段名称,最大长度32
  @retval data        字段内容
  @retval error       错误码
*/
func (r *Record) GetValueInt8(name string) (int8, error)
func (r *Record) GetValueInt16(name string) (int16, error)
func (r *Record) GetValueInt32(name string) (int32, error)
func (r *Record) GetValueInt64(name string) (int64, error)
func (r *Record) GetValueFloat32(name string) (float32, error)
func (r *Record) GetValueFloat64(name string) (float64, error)
func (r *Record) GetValueStr(name string) (string, error)
func (r *Record) GetValueBlob(name string) ([]byte, error)

8. 常见问题

详见错误码含义和处理方法

8. 其它参考文档

[TDR Generic表][C++ SDK]查询单条数据接口说明

[TDR Generic表][Java SDK]查询单条数据接口说明

[TDR Generic表][MySQL协议兼容接口]查询数据接口说明

results matching ""

    No results matching ""